//
// Created by wopy on 2020/9/2.
//

#include "Table.hpp"
#include "InputState.hpp"
#include "Utils.hpp"
#include <fstream>
#include <iostream>
#include <algorithm>

void *Table::context = nullptr;

Table::~Table() {
    save();
}

Table::Table(string path, TABLETYPE type) {
    if (path.empty()) {
        return;
    }

    ifstream in(path);

    if (in.is_open()) {
        try {
            Json::CharReaderBuilder builder;
            builder["collectComments"] = false;
            JSONCPP_STRING errs;
            Json::parseFromStream(builder, in, &root, &errs);
            in.close();
        } catch (Json::LogicError &error) {
            cout << "" << endl;
            cout << error.what() << endl;
            this->type = TABLE_ERROR;
            return;
        }
    }

    this->path = path;
    this->type = type;
}

void Table::save() {
    if (root.empty()) {
        return;
    }
    ofstream out(path);
    if (!!!out.is_open()) {
        return;
    }
    out << root << endl;
    out.close();
}

int Table::getWordListForKeyCode(const char *keyCode, FcitxCandidateWordList *dstList,
                                 FcitxCandidateWordCommitCallback callback) {
    if (type != TABLE_TYPE_CORE) {
        return 0;
    }

    int n = 0;

    SortList preList;

    for(auto & item : root) {
        if (!!!matchKeyCode(keyCode, item["key"].asString().c_str())) {
            continue;
        }

        FcitxCandidateWord candidateWord;
        if (make(item, 0, &candidateWord)) {
            candidateWord.callback = callback;
            preList.push_back(candidateWord);
            n++;
        }
    }

    sort(preList, dstList);

    return n;
}

int Table::getWordListForPrefix(const char *prefix, FcitxCandidateWordList * dstList
                                 , FcitxCandidateWordCommitCallback callback) {

    int pSize = strlen(prefix);

    if (type == TABLE_TYPE_CORE || pSize < 1) {
        return 0;
    }

    int n = 0;

    SortList preList;

    for(auto & item : root) {
        const char * word = item["word"].asString().c_str();

        if (strncmp(prefix, word, pSize) || strlen(word) == pSize) {
            continue;
        }

        FcitxCandidateWord candidateWord;
        if (make(item, pSize, &candidateWord)) {
            candidateWord.callback = callback;
            preList.push_back(candidateWord);
            n++;
        }
    }

    sort(preList, dstList);

    return n;
}

FcitxCandidateWord *Table::make(Value &value, int pSize
                                     , FcitxCandidateWord *dstWord) {
    const char *word = value["word"].asString().c_str();
    if (strlen(word) < 1 || dstWord == nullptr) {
        return nullptr;
    }
    int *freq = new int;
    *freq = value["freq"].asInt();

    dstWord->owner = context;
    dstWord->strExtra = nullptr;
    dstWord->priv = freq;
    dstWord->strWord = strdup(word + pSize);
    dstWord->wordType = MSG_OTHER;

    return dstWord;
}

void Table::updateFreq(const char *word) {
    if (!!!word) {
        return;
    }
    int len = strlen(word);
    if (len < 1) {
        return;
    }

    for (ValueIterator iterator = root.begin();iterator != root.end();iterator ++) {
        Value &item = *iterator;
        const char *curWord = item["word"].asString().c_str();
        if (len == strlen(curWord)) {
            if (!!!strcmp(word, curWord)) {
                int freq = item["freq"].asInt();
                item["freq"] = freq + freq % 10000;
                return;
            }
        }
    }
}

void Table::sort(SortList &srcList, FcitxCandidateWordList *dstList) {

    int count = srcList.size();

    if (count < 1) {
        return;
    }

    std::sort(srcList.begin(), srcList.end());

    for (int max = maxLen(srcList); max > 0; max --) {
        sortForLen(srcList, dstList, max);
    }

}

void Table::setContext(void *context) {
    Table::context = context;
}

int Table::maxLen(const SortList & sortList) {
    int max = 0;
    for (auto &word : sortList) {
        int len = fcitx_utf8_strlen(word.strWord);
        if (len > max) {
            max = len;
        }
    }
    return max;
}

void Table::sortForLen(SortList &srcList, FcitxCandidateWordList *dstList, const int len) {
    int n = 0;
    for (auto & item : srcList){
        if (fcitx_utf8_strlen(item.strWord) == len) {
            FcitxCandidateWordInsert(dstList, &item, 0);
            n++;
        }
    }
}

bool operator<(const FcitxCandidateWord &w1, const FcitxCandidateWord &w2) {
    int fq1 = *(int *) w1.priv;
    int fq2 = *(int *) w2.priv;

    return fq1 < fq2;
}
